<?php
namespace jm\helper;

class Secure
{
    private static $expires = 7200;
    private static $promptDiff = 60;
    private static $fixedParams = [
        'project' => 'yunjing',
        'company' => 'yunxiang',
        'location' => 'shandong'
    ];
    // company=yunxiang&location=shandong&nonce=123456&project=yunjing&timestamp=15512345678

    // 生成 signature 时要求两个参数，分别是 nonce、timestamp；
    // 生成 token 时要求三个参数，分别是 nonce、timestamp、signature；
    private static function _getCommon() {
        $args = func_get_args();
        $suffix = ['nonce' => $args[0], 'timestamp' => $args[1]];
        if (count($suffix) === 3) {
            $suffix['signature'] = $args[3];
        }
        $arr = array_merge(self::$fixedParams, $suffix);
        ksort($arr);
        $urlParams = [];
        array_walk($arr, function($value, $key) use (&$urlParams) {
            array_push($urlParams, $key . '=' . $value);
        });
        return $urlParams;
    }

    private static function _getSignatureBase64($nonce, $timestamp) {
        return base64_encode(implode('&', $this->_getCommon($nonce, $timestamp)));
    }

    private static function _getSignatureBase64UpperCase($nonce, $timestamp) {
        return strtoupper(self::_getSignatureBase64($nonce, $timestamp));
    }

    private static function _getTokenBase64($nonce, $timestamp, $signature) {
        return base64_encode(implode('&', self::_getCommon($nonce, $timestamp, $signature)));
    }

    private static function _getTokenBase64UpperCase($nonce, $timestamp, $signature) {
        return strtoupper(self::_getTokenBase64($nonce, $timestamp, $signature));
    }

    private static function _getToken($nonce, $timestamp, $signature) {
        return [
            'expires' => self::$expires,
            'token' => self::_getTokenBase64UpperCase($nonce, $timestamp, $signature),
            'token_time' => DateTime::YMDHISWithLine()
        ];
    }

    // 参数中的 authtype 只是用于迷惑抓包者，不用于计算signature和token；
    // 验证 signature，生成 token 给客户端；
    public function if() {
        try {
            // 注册前获取token，此时还没有account_id；
            // 假如用户退出App，清理本地信息，导致token和account_id都没有了，这和第一种情况一样了；
            // 什么两个情况，都应该在登录时删除旧token，更新新token到用户表；
            // 当使用过程中，token过期了，获取新token，删除旧token--此时需要account_id，而且一定存在，直接更新用户表；
            $param = $this->jmParameter(['account_id', 'nonce', 'timestamp', 'authtype', 'signature']);
            $validateResult = $this->validate(
                $param, 
                [
                    'nonce' => 'require',
                    'timestamp' => 'require',
                    'authstring' => 'require',
                    'signature' => 'require'
                ]
            );
            if (true !== $validateResult) {
                return json(['errcode'=>4100, 'message'=>$validateResult]);
            }

            $serverSignature = $this->_getSignatureBase64UpperCase($param['nonce'], $param['timestamp']);
            // \Dev::ve([$param['signature'], $serverSignature]);
            if ($param['signature'] !== $serverSignature) {
                return ['errcode'=>4101, 'message'=>'签名无效'];
            }

            $tokenInfo = $this->_getToken($param['nonce'], $param['timestamp'], $param['signature']);

            // 新增token，
            $insertResult = TokenModel::insert(['token' => $tokenInfo['token'], 'token_time' => $tokenInfo['token_time']]);
            if (empty($insertResult)) {
                return $this->jmReturnJson(4102, 'token保存失败，请重试');
            }
            
            // 删除旧token
            if (isset($param['account_id'])) {
                $account = AccountModel::field(['id','token'])->find($param['account_id'] * 1);
                $oldToken = $account->token;
                $account->token = $tokenInfo['token'];
                $row = $account->isUpdate(true)->save();
                // 更新成功，并且有旧的token则删除；
                if (!empty($row) && !empty($oldToken)) {
                    // 真实删除，一维结果无关紧要，可以不做判断
                    TokenModel::where('token', $oldToken)->delete();
                }
            }

            $tokenInfo['token_time'] = strtotime($tokenInfo['token_time']);

            return $this->jmReturnJson(200, '操作成功', $tokenInfo);
        } catch (\Exception $e) {
            return $this->jmException($e);
        }
    }

    public static function checkSignature($nonce, $timestamp) {
        $serverSignature = self::_getSignatureBase64UpperCase($param['nonce'], $param['timestamp']);
        // \Dev::ve([$param['signature'], $serverSignature]);
        if ($param['signature'] !== $serverSignature) {
            return ['errcode'=>4101, 'message'=>'签名无效'];
        }
    }
}